#ifndef GAMEENGINE_GEOMETRY_VECTOR3D_HPP
#define GAMEENGINE_GEOMETRY_VECTOR3D_HPP

#include <math.h>

#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif

//=================================================================================
// Implementation of the Vector3d class

//! Default constructor
/*!
 @note Class T should have a default constructor
 */
template <class T>
Vector3d<T>::Vector3d() : Point3d<T>() {}

//! Constructor from two points
/*!
 @param origin The origin of the vector
 @param destination The destination of the vector
 @note Class T should have a default constructor
 @note Class T should have an assignment operator
 */
template <class T>
Vector3d<T>::Vector3d(const Point3d<T>& origin, const Point3d<T>& destination) :
	Point3d<T>(destination.x()-origin.x(),destination.y()-origin.y(),destination.z()-origin.z()) {}

//! Constructor from three coordinates
/*!
 @param x The first coordinate
 @param y The second coordinate
 @param z The third coordinate
 @note Class T should have a default constructor
 @note Class T should have an assignment operator
 */
template <class T>
Vector3d<T>::Vector3d(const T& x, const T& y, const T& z) : Point3d<T>(x,y,z) {}

//! Copy constructor
/*!
 @param rhs The point to copy
 @note Class T should have a default constructor
 @note Class T should have an assignment operator
 */
template <class T>
template <class R>
Vector3d<T>::Vector3d(const Point3d<R>& rhs) : Point3d<T>() {
	for ( int k = 0; k < 3; ++k ) {
		coordinates_[k] = (T)rhs[(Point3D::Index)k];
	}
}

//! Copy constructor
/*!
 @param rhs The vector to copy
 @note Class T should have a default constructor
 @note Class T should have an assignment operator
 */
template <class T>
Vector3d<T>::Vector3d(const Vector3d& rhs) : Point3d<T>(rhs) {}

//! Destructor
/*!
 @note The destructor of class T should be public
 */
template <class T>
Vector3d<T>::~Vector3d() {}

//! Assignment operator
/*!
 @param rhs The vector to copy
 @note Class T should have an assignment operator
 */
template <class T>
Vector3d<T>& Vector3d<T>::operator=(const Vector3d& rhs) {
	if ( &rhs != this ) {
		for ( int k = 0; k < 3; ++k ) {
			coordinates_[k] = rhs.coordinates_[k];
		}
	}
	return *this;
}

//! Plus operator
/*!
 @param rhs The vector to use for the offset
 @note Class R should have implicit conversion to class T
 @note Class T should have a plus operator
 */
template <class T>
Vector3d<T>& Vector3d<T>::operator+=(const Vector3d& rhs) {
	for ( int k = 0; k < 3; ++k ) {
		coordinates_[k] += rhs.coordinates_[k];
	}
	return *this;
}

//! Minus operator
/*!
 @param rhs The vector to use for the offset
 @note Class R should have implicit conversion to class T
 @note Class T should have a plus operator
 */
template <class T>
Vector3d<T>& Vector3d<T>::operator-=(const Vector3d& rhs) {
	for ( int k = 0; k < 3; ++k ) {
		coordinates_[k] -= rhs.coordinates_[k];
	}
	return *this;
}

//! Multiplication operator
/*!
 @param rhs The vector to use for the multiplication
 @note Class R should have implicit conversion to class T
 @note Class T should have a multiplication operator
 */
template <class T>
Vector3d<T>& Vector3d<T>::operator*=(const Vector3d& rhs) {
	for ( int k = 0; k < 3; ++k ) {
		coordinates_[k] *= rhs.coordinates_[k];
	}
	return *this;
}

//! Plus operator
/*!
 @param rhs The vector to use for the offset
 @note Class R should have implicit conversion to class T
 @note Class T should have a plus operator
 */
template <class T>
Vector3d<T> Vector3d<T>::operator+(const Vector3d& rhs) const {
	Vector3d<T> res(*this);
	for ( int k = 0; k < 3; ++k ) {
		res.coordinates_[k] += rhs.coordinates_[k];
	}
	return res;
}

//! Minus operator
/*!
 @param rhs The vector to use for the offset
 @note Class R should have implicit conversion to class T
 @note Class T should have a plus operator
 */
template <class T>
Vector3d<T> Vector3d<T>::operator-(const Vector3d& rhs) const {
	Vector3d<T> res(*this);
	for ( int k = 0; k < 3; ++k ) {
		res.coordinates_[k] -= rhs.coordinates_[k];
	}
	return res;
}

//! Multiplication operator
/*!
 @param rhs The vector to use for the multiplication
 @note Class R should have implicit conversion to class T
 @note Class T should have a multiplication operator
 */
template <class T>
Vector3d<T> Vector3d<T>::operator*(const Vector3d& rhs) const {
	Vector3d<T> res(*this);
	for ( int k = 0; k < 3; ++k ) {
		res.coordinates_[k] *= rhs.coordinates_[k];
	}
	return res;
}

//! Plus operator
/*!
 @param rhs The vector to use for the offset
 @note Class R should have implicit conversion to class T
 @note Class T should have a plus operator
 */
template <class T>
template <class R>
Vector3d<T>& Vector3d<T>::operator+=(const Vector3d<R>& rhs) {
	for ( int k = 0; k < 3; ++k ) {
		coordinates_[k] += (T)rhs[(Point3D::Index)k];
	}
	return *this;
}

//! Minus operator
/*!
 @param rhs The vector to use for the offset
 @note Class R should have implicit conversion to class T
 @note Class T should have a plus operator
 */
template <class T>
template <class R>
Vector3d<T>& Vector3d<T>::operator-=(const Vector3d<R>& rhs) {
	for ( int k = 0; k < 3; ++k ) {
		coordinates_[k] -= (T)rhs[(Point3D::Index)k];
	}
	return *this;
}

//! Multiplication operator
/*!
 @param rhs The vector to use for the multiplication
 @note Class R should have implicit conversion to class T
 @note Class T should have a multiplication operator
 */
template <class T>
template <class R>
Vector3d<T>& Vector3d<T>::operator*=(const Vector3d<R>& rhs) {
	for ( int k = 0; k < 3; ++k ) {
		coordinates_[k] *= (T)rhs[(Point3D::Index)k];
	}
	return *this;
}

//! Plus operator
/*!
 @param rhs The vector to use for the offset
 @note Class R should have implicit conversion to class T
 @note Class T should have a plus operator
 */
template <class T>
template <class R>
Vector3d<T> Vector3d<T>::operator+(const Vector3d<R>& rhs) const {
	Vector3d<T> res(*this);
	for ( int k = 0; k < 3; ++k ) {
		res.coordinates_[k] += (T)rhs[(Point3D::Index)k];
	}
	return res;
}

//! Minus operator
/*!
 @param rhs The vector to use for the offset
 @note Class R should have implicit conversion to class T
 @note Class T should have a plus operator
 */
template <class T>
template <class R>
Vector3d<T> Vector3d<T>::operator-(const Vector3d<R>& rhs) const {
	Vector3d<T> res(*this);
	for ( int k = 0; k < 3; ++k ) {
		res.coordinates_[k] -= (T)rhs[(Point3D::Index)k];
	}
	return res;
}

//! Multiplication operator
/*!
 @param rhs The vector to use for the multiplication
 @note Class R should have implicit conversion to class T
 @note Class T should have a multiplication operator
 */
template <class T>
template <class R>
Vector3d<T> Vector3d<T>::operator*(const Vector3d<R>& rhs) const {
	Vector3d<T> res(*this);
	for ( int k = 0; k < 3; ++k ) {
		res.coordinates_[k] *= (T)rhs[(Point3D::Index)k];
	}
	return res;
}

//! Multiplication operator
/*!
 @param rhs The value to use for the multiplication
 @note Class R should have implicit conversion to class T
 @note Class T should have a multiplication operator
 */
template <class T>
template <class R>
Vector3d<T>& Vector3d<T>::operator*=(const R& rhs) {
	T mult = (T)rhs;
	for ( int k = 0; k < 3; ++k ) {
		coordinates_[k] *= mult;
	}
	return *this;
}

//! Division operator
/*!
 @param rhs The value to use for the division
 @note Class R should have implicit conversion to class T
 @note Class T should have a division operator
 */
template <class T>
template <class R>
Vector3d<T>& Vector3d<T>::operator/=(const R& rhs) {
	T div = (T)rhs;
	for ( int k = 0; k < 3; ++k ) {
		coordinates_[k] /= div;
	}
	return *this;
}

//! Multiplication operator
/*!
 @param rhs The value to use for the multiplication
 @note Class R should have implicit conversion to class T
 @note Class T should have a multiplication operator
 */
template <class T>
template <class R>
Vector3d<T> Vector3d<T>::operator*(const R& rhs) const {
	Vector3d<T> res(*this);
	T div = (T)rhs;
	for ( int k = 0; k < 3; ++k ) {
		res.coordinates_[k] *= div;
	}
	return res;
}

//! Division operator
/*!
 @param rhs The value to use for the division
 @note Class R should have implicit conversion to class T
 @note Class T should have a division operator
 */
template <class T>
template <class R>
Vector3d<T> Vector3d<T>::operator/(const R& rhs) const {
	Vector3d<T> res(*this);
	T div = (T)rhs;
	for ( int k = 0; k < 3; ++k ) {
		res.coordinates_[k] /= div;
	}
	return res;
}

//! Multiplication operator
/*!
 @param lhs The value to use for the multiplication
 @param rhs The vector to use for the multiplication
 @note Class R should have implicit conversion to class T
 @note Class T should have a multiplication operator
 */
template <class R, class T>
Vector3d<T> operator*(const R& lhs, const Vector3d<T>& rhs) {
	Vector3d<T> res(rhs);
	T div = (T)lhs;
	for ( int k = 0; k < 3; ++k ) {
		res.coordinates_[k] *= div;
	}
	return res;
}

//! Division operator
/*!
 @param lhs The value to use for the division
 @param rhs The vector to use for the division
 @note Class R should have implicit conversion to class T
 @note Class T should have a division operator
 */
template <class R, class T>
Vector3d<T> operator/(const R& lhs, const Vector3d<T>& rhs) {
	Vector3d<T> res(rhs);
	T div = (T)lhs;
	for ( int k = 0; k < 3; ++k ) {
		res.coordinates_[k] /= div;
	}
	return res;
}

//! Dot product operator
/*!
 @param v0 The first vector
 @param v1 The second vector
 @note Class T should have an assignment operator
 */
template <class T>
T Vector3d<T>::dot(const Vector3d& v0, const Vector3d& v1) {
	return v0.dot(v1);
}

//! Dot product operator
/*!
 @param rhs The first vector
 @note Class T should have an assignment operator
 */
template <class T>
T Vector3d<T>::dot(const Vector3d& rhs) const {
	return coordinates_[0]*rhs.coordinates_[0] + coordinates_[1]*rhs.coordinates_[1] + coordinates_[2]*rhs.coordinates_[2];
}

//! Cross product operator
/*!
 @param v0 The first vector
 @param v1 The second vector
 @note Class T should have an assignment operator
 */
template <class T>
Vector3d<T> Vector3d<T>::cross(const Vector3d& v0, const Vector3d& v1) {
	return v0.cross(v1);
}

//! Cross product operator
/*!
 @param rhs The first vector
 @note Class T should have an assignment operator
 */
template <class T>
Vector3d<T> Vector3d<T>::cross(const Vector3d& rhs) const {
	return Vector3d<T>(
		coordinates_[1]*rhs.coordinates_[2] - rhs.coordinates_[1]*coordinates_[2],
		rhs.coordinates_[0]*coordinates_[2] - coordinates_[0]*rhs.coordinates_[2],
		coordinates_[0]*rhs.coordinates_[1] - rhs.coordinates_[0]*coordinates_[1]);
}

//! Test whether two vectors are colinear
/*!
 @param v0 The first vector
 @param v1 The second vector
 @param tolerance The tolerance for collinearity of the two vectors
 @note The two vectors are considered collinear if the square length
       of their cross product is below the given tolerance
 @note Class T should have an assignment operator
 */
template <class T>
bool Vector3d<T>::collinear(const Vector3d& v0, const Vector3d& v1, const T& tolerance) {
	return v0.collinear(v1, tolerance);
}

//! Cross product operator
/*!
 @param rhs The first vector
 @param tolerance The tolerance for collinearity of the two vectors
 @note The two vectors are considered collinear if the square length
       of their cross product is below the given tolerance
 @note Class T should have an assignment operator
 */
template <class T>
bool Vector3d<T>::collinear(const Vector3d& rhs, const T& tolerance) const {
	return ( cross(rhs).square_length() <= tolerance );
}

//! Normalize the vector
/*!
 @note Class T should have a multiplication operator
 @note Class T should be compatible with the sqrt function defined in math.h
 */
template <class T>
Vector3d<T>& Vector3d<T>::normalize() {
	return operator/=( length() );
}

//! Normalize the vector
/*!
 @note Class T should have a multiplication operator
 @note Class T should be compatible with the sqrt function defined in math.h
 */
template <class T>
Vector3d<T> Vector3d<T>::normalize() const {
	return Vector3d<T>(*this) / length();
}

//! Square length of the vector
/*!
 @note Class T should have a multiplication operator
 */
template <class T>
T Vector3d<T>::square_length() const {
	return coordinates_[0]*coordinates_[0] + coordinates_[1]*coordinates_[1] + coordinates_[2]*coordinates_[2];
}

//! Length of the vector
/*!
 @note Class T should have a multiplication operator
 @note Class T should be compatible with the sqrt function defined in math.h
 */
template <class T>
T Vector3d<T>::length() const {
	return sqrt( square_length() );
}

#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif

#endif
